﻿Imports EnhancedSerialPort
Imports System.IO.Ports
Imports System.Threading
Imports System
Imports System.Runtime.InteropServices
Imports System.Xml
Imports System.Xml.Linq
Imports System.IO
Imports System.Reflection
Imports System.Linq
Imports System.Text
Imports System.ComponentModel
Imports System.Diagnostics

Public Class PropIO

#Region "Variables and Enumerations"
    'Host Form Controls
    Private Property HostForm As Form1
    Public Property RXStatusControl As Control
    Public Property RXKrackerFWControl As Control
    'Private Properties
    '    Public Shared xmldoc As XDocument = XDocument.Load(New StreamReader([Assembly].GetEntryAssembly.GetManifestResourceStream("WindowsApplication1.Krackerelements.xml")))
    Public Property xmldoc As XDocument

#End Region

#Region "Properties"
    Private _KrackerMode As Byte = KrackerModes.NotAvailable
    Private _ComNumber As Integer = 0
    Private _PropDLLVersion As UInteger
    Private _KrackerFWNum As Decimal
    Private _ConnectionState As Byte = ConnectionStates.NotLoaded


    Private Property KrackerMode As KrackerModes
        Get
            Return _KrackerMode
        End Get
        Set(value As KrackerModes)
            Dim dictr As Dictionary(Of Byte, String) = Nothing
            If value <> _KrackerMode Then
                Debug.Write("Property: KrackerMode Prev:" & _KrackerMode.ToString & " New:" & value.ToString)
                'If the previous mode was notavailable and we've got a recent firmware, turn off interactive mode 
                SerialLineOut(If((_KrackerMode = KrackerModes.NotAvailable And KrackerFWNum > 0.79) = True, "M9", Nothing))
                'Exit from current mode
                dictr = New Dictionary(Of Byte, String) From {{KrackerModes.PCdiag, "Mx"}, {KrackerModes.SerialMonitor, "seroff"}} ' How to get to mode from each listed mode
                SerialLineOut(If(dictr.ContainsKey(value) = True, dictr.Item(value), Nothing))

                'Now go from config mode to the desired mode
                dictr = New Dictionary(Of Byte, String) From {{KrackerModes.PCdiag, "M3"}, {KrackerModes.SerialMonitor, "M1"}}
                SerialLineOut(If(dictr.ContainsKey(value) = True, dictr.Item(value), Nothing))
                _KrackerMode = value
            End If
        End Set
    End Property

    Public Property KrackerFWNum As Decimal
        Get
            Return _KrackerFWNum
        End Get
        Private Set(value As Decimal)
            _KrackerFWNum = value
            Debug.WriteLine("krackerFW set to " & value.ToString)
        End Set
    End Property
    Public Property ComPort As Integer
        Get
            Return _ComNumber
        End Get
        Private Set(value As Integer)
            _ComNumber = value
        End Set
    End Property
    Public Property PropDLLVersion As UInteger
        Get
            Return _PropDLLVersion
        End Get
        Private Set(value As UInteger)
            _PropDLLVersion = value
        End Set
    End Property


#End Region
    ''' <summary>
    ''' Current Connection State
    ''' </summary>
    ''' <value>Me.ConnectionStates</value>
    ''' <returns>ConnectionStates</returns>
    ''' <remarks></remarks>
    Public Property ConnectionState As ConnectionStates
        Get
            Return _ConnectionState
        End Get
        Private Set(value As ConnectionStates)
            _ConnectionState = value
            Debug.WriteLine("Property: ConnectionState: " & value.ToString)

        End Set
    End Property




#Region "Events and Delegates"
#Region "Public Events"
    Public Shared Event DataReceived(BytesInBuffer As Integer)
    Public Shared Event ConnectionStateChanged(newstate As Byte)
#End Region
#Region "Private Events"
    Private Shared Event constatechanged(newstate As Byte)
    Private WithEvents pport As New EnhancedSerialPort.SerialPort With {.Parity = Parity.None, .NewLine = vbCr, .StopBits = StopBits.One, .BaudRate = 115200, .ReadTimeout = -1, .WriteTimeout = -1, .DataBits = 8, .ReceivedBytesThreshold = 1, .WatchdogTimeout = 10000, .WatchdogReopenTime = 30000, .EnableWatchdog = False, .AttemptPortReopenOnWatchdogTimeout = False}
#End Region
#Region "Delegates"
    Delegate Sub ControlRefreshDelegate(ctrl As Object, valstorefresh As List(Of PropertyPairs))
    Delegate Sub reconnectdelegate()

    Public Function GetControlID(ctrl As Object) As Integer
        Dim cttype As Type = ctrl.GetType
        Dim ctrltypename As String = cttype.Name
        Dim fullname As String = ctrl.Name
        Dim Controlnum As Integer = fullname.Substring(ctrltypename.Length)
        Dim dictr As New Dictionary(Of String, Integer) From {{"ComboBox", 100}, {"RadioButton", 200}, {"CheckBox", 300}}
        Controlnum += If(dictr.ContainsKey(ctrltypename) = True, dictr.Item(ctrltypename), 0)
        Return Controlnum
    End Function
#End Region
#End Region
    Public bg As BackgroundWorker
    Public reconnect As reconnectdelegate = New reconnectdelegate(AddressOf ComportSearch)


    Public Sub New(ByRef sender As Form)
        HostForm = sender
        PropellentDLL.Start(HostForm.Handle, False, Nothing)
        RaiseEvent constatechanged(ConnectionStates.PropellentLoaded)
        Me.PropDLLVersion = PropellentDLL.VersionCheck
        ComportSearch()

        Debug.WriteLine("")


    End Sub
    Private Sub ComportSearch()
        PropellentDLL.SetGUI(PropellentDLL.GuiMode.AllOff)
        Dim Tempport As Integer
        Dim comstring = PropellentDLL.PropVersion()
        Dim ComStringLocator = comstring.IndexOf(" ")
        If ComStringLocator = 4 Or ComStringLocator = 5 Then
            Tempport = Val(comstring.Chars(ComStringLocator - 1))
            If ComStringLocator = 5 Then
                Tempport = Tempport + Val(comstring.Chars(ComStringLocator - 2)) * 10
            End If
        End If

        Debug.WriteLine(If(Tempport = 0, ("No Prop Found: Response:" & comstring), "Propeller Found on COM" & Tempport.ToString))
        '    Public WithEvents BG As New BackgroundWorker With {.WorkerSupportsCancellation = True, .WorkerReportsProgress = False}

        If Tempport = 0 Then
            Using bg As New BackgroundWorker With {.WorkerSupportsCancellation = True, .WorkerReportsProgress = False}
                AddHandler bg.DoWork, Sub(f, r)
                                          Dim worker As BackgroundWorker = CType(f, BackgroundWorker)
                                          Thread.CurrentThread.Name = "BG Waiter"
                                          Dim loops = CInt(r.Argument) / 10
                                          For i As Integer = 0 To 10
                                              Thread.Sleep(loops)
                                              If worker.CancellationPending = True Then
                                                  r.Cancel = True
                                                  Debug.WriteLine("BG1 cancelled")
                                                  Return
                                              End If
                                          Next
                                      End Sub
                AddHandler bg.RunWorkerCompleted, Sub(f, r) ComportSearch()
                Me.bg = bg
                bg.RunWorkerAsync(1000)
            End Using

        Else
            Me.ComPort = Tempport
            PropellentDLL.SetGUI(PropellentDLL.GuiMode.ShowDialog)
            With pport
                .PortName = ("COM" & Me.ComPort.ToString)
                .Open()
            End With
            RaiseEvent constatechanged(ConnectionStates.Connecting)

            Using bg As New BackgroundWorker With {.WorkerSupportsCancellation = True, .WorkerReportsProgress = False}
                AddHandler bg.DoWork, Sub(f, r)
                                          Dim worker As BackgroundWorker = CType(f, BackgroundWorker)
                                          Dim loops = CInt(r.Argument) / 30
                                          For i As Integer = 0 To 30
                                              Thread.Sleep(CInt(loops))
                                              If worker.CancellationPending = True Then
                                                  Debug.WriteLine("BG1 cancelled")
                                                  r.Cancel = True
                                                  Return
                                              End If
                                          Next

                                          Dim Versionstr As Decimal = 0
                                          Dim str As String = ReadSerialLine("&&")
                                          If str.Chars(0) = "V" Or str.Chars(0) = "D" Then
                                              Versionstr = Convert.ToDecimal(ReadSerialLine("&&"))
                                              SerialLineOut("C")
                                          End If
                                          r.Result = Versionstr
                                      End Sub
                AddHandler bg.RunWorkerCompleted, Sub(f, r)
                                                      Dim version As Decimal = CDec(r.Result)
                                                      KrackerFWNum = version
                                                      If KrackerFWNum = 0 Then
                                                          RaiseEvent constatechanged(ConnectionStates.OtherFW)
                                                      Else
                                                          KrackerMode = KrackerModes.Config
                                                          RaiseEvent constatechanged(ConnectionStates.KrackerFW)
                                                      End If
                                                  End Sub
                Me.bg = bg
                bg.RunWorkerAsync(3000)
            End Using
        End If
    End Sub

    Private Shared SerialLockFlag As Object = New Object
    Private SetSerialLock As Func(Of Boolean) = Function()
                                                    Dim x As Boolean = Monitor.TryEnter(SerialLockFlag, 3000)
                                                    If Not x Then
                                                        Debug.WriteLine("Lock Failed: Timeout")
                                                    End If
                                                    Return x
                                                End Function

    Private releaseSerialLock As Func(Of Boolean) = Function()
                                                        Try
                                                            Monitor.Exit(SerialLockFlag)
                                                        Catch ex As SynchronizationLockException
                                                            Debug.WriteLine("Lock Failed: Didn't Have any lock")
                                                        End Try
                                                        Return True
                                                    End Function



    ''' <summary>
    ''' Read Raw line from Serial Buffer, Carriage Return terminated
    ''' </summary>
    ''' <returns>String received</returns>
    ''' <remarks></remarks>
    Public Overloads Function ReadSerialLine() As String
        Dim x As String = Nothing
        If SetSerialLock() Then
            x = unsafeReadSerialport(DataType.RawLine, Nothing)
            releaseSerialLock()
        End If
        Return x
    End Function

    ''' <summary>
    ''' Read Line from Serial buffer, CR terminated startng at the pattern stringprefix
    ''' </summary>
    ''' <param name="StringPrefix">Prefix that indicates a string is being received</param>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Overloads Function ReadSerialLine(StringPrefix As String) As String
        Dim x As String = Nothing
        If SetSerialLock() Then
            x = unsafeReadSerialport(DataType.PrefixedLine, StringPrefix)
            releaseSerialLock()
        End If
        Return x
    End Function

    Public Function ReadSerialCharacter() As Char
        Dim x As Char = vbNullChar, result As String
        If SetSerialLock() Then
            result = unsafeReadSerialport(DataType.Character, Nothing)
            x = result.Chars(0)
            releaseSerialLock()
        End If
        Return x
    End Function



    Private Function unsafeReadSerialport(readtype As DataType, stringprefix As String) As String
        Dim linetimer As Integer = 0
        Try
            Do While linetimer < 900
                If (pport.BytesToRead = 0) Or (pport.BytesToRead < stringprefix.Length And readtype = DataType.PrefixedLine) Then
                    linetimer += 50
                    Thread.Sleep(50)
                Else
                    Select Case readtype
                        Case DataType.Character
                            Return Convert.ToString(pport.ReadChar)
                        Case DataType.RawLine
                            Return pport.ReadLine
                        Case DataType.PrefixedLine
                            Dim dontmatch As Boolean = False
                            For index = 0 To (stringprefix.Length - 1)
                                dontmatch = dontmatch Or If((pport.ReadChar() <> Asc(stringprefix.Chars(index))), True, False)
                            Next
                            If dontmatch = False Then
                                Return pport.ReadLine
                            End If
                    End Select
                End If
            Loop
        Catch
            Debug.WriteLine("Thread: Co")
        End Try
        Return vbNullString
    End Function
    Enum DataType
        Character
        RawLine
        PrefixedLine
    End Enum

    Private Sub WRITEunsafeSerialport(str As String, WriteMode As WriteFormat)
        Try
            If WriteMode = WriteFormat.AsChar Then
                `1
                pport.Write(str)
            ElseIf WriteMode = WriteFormat.AsString Then
                pport.WriteLine(str)


            End If

        Catch ex As Exception

        End Try
    End Sub
    Enum WriteFormat
        ''' <summary>
        ''' Send Character without 
        ''' </summary>
        ''' <remarks></remarks>
        AsChar
        ''' <summary>
        ''' Send String Only
        ''' </summary>
        ''' <remarks></remarks>
        AsString
        ''' <summary>
        ''' Do not add a Carriage Return when sending
        ''' </summary>
        ''' <remarks></remarks>
        AsLine
    End Enum


    Public Sub SerialLineOut(str As String)
        Try
            pport.WriteLine(str)
        Catch ex As Exception
            Debug.WriteLine("Failed to write to serial")
        End Try



    End Sub

    Public Function SetupDiagnosticControl(ctrl As DiagnosticControl)
        'Diagnostic CTRL displays 
        ' Text  or number as text
        ' slider
        ' slider 

        Using bg As New BackgroundWorker
            AddHandler bg.DoWork, Sub(f, r) Thread.Sleep(CInt(r.Argument))
            AddHandler bg.RunWorkerCompleted, Sub(f, r) ComportSearch()
            bg.RunWorkerAsync(1000)
        End Using
        Return 0
    End Function

    ''' <summary>
    ''' Read data stored remotely
    ''' </summary>
    ''' <returns>Integer</returns>
    ''' <remarks></remarks>
    ''' <param name="controlsel">Remote Storage Location for Control Value</param>
    Public Overloads Function GetRemote(ByVal controlsel As Integer) As Integer
        Dim str As String = Nothing
        Dim response As Integer

        SerialLineOut("Gb" & Convert.ToString(controlsel))
        str = ReadSerialLine("&&")
        response = Convert.ToInt32(str)
        'SerialPort1.DiscardInBuffer()
        Debug.WriteIf(controlsel = 999, "String DB Version Read as " & response.ToString)
        Return response
    End Function

    Public Overloads Function GetRemote(ByVal Ctrl As Integer, AssetGroup As AssetCategory)
        If SetSerialLock() Then


            x = unsafeReadSerialport(SerialPortReadType.PrefixedLine, StringPrefix)
            releaseSerialLock()
        End If



    End Function

    Enum AssetCategory
        Strings
        ByteArray
        WordArray
        LongArray, longerarray
        ByteSingleton
        WordSingleton
        LongSingleton
        FloatSingleton
    End Enum

    ''' <summary>
    ''' Read data stored remotely
    ''' </summary>
    ''' <returns>Integer</returns>
    ''' <remarks></remarks>
    ''' <param name="Ctrl">Control to lookup</param>
    Public Overloads Function GetRemote(Ctrl As Control) As Integer
        'KrackerMode = KrackerModes.Config
        Dim str As String = Nothing
        Dim response As Integer

        SerialLineOut("Gb" & Convert.ToString(GetControlID(Ctrl)))
        str = ReadSerialLine("&&")
        response = Convert.ToInt32(str)
        Return response

    End Function

    Public Overloads Sub setremote(ByVal controlsel As Integer, ByVal controlvalue As Integer)

    End Sub

    Public Overloads Sub setremote(ctrl As Control)
        'KrackerMode = KrackerModes.Config
        Dim cttype As Type = ctrl.GetType
        Dim ctrltypename As String = cttype.Name
        SerialLineOut("Sb" & Me.GetControlID(ctrl).ToString)
        Select Case ctrltypename
            Case "ComboBox"
                Dim tbox As ComboBox = CType(ctrl, ComboBox)
                SerialLineOut(Convert.ToSByte(tbox.SelectedIndex))
            Case "RadioButton"
                Dim tbox As RadioButton = CType(ctrl, RadioButton)
                SerialLineOut(Convert.ToSByte(tbox.Checked))
            Case "CheckBox"
                Dim tbox As CheckBox = CType(ctrl, CheckBox)
                SerialLineOut(Convert.ToSByte(tbox.Checked))
        End Select
        ReadSerialLine()
    End Sub

    Public Sub HousekeepingCtrls(SelectedFunction As HousekeepingFunctions)
        KrackerMode = KrackerModes.Config
        SerialLineOut("M" & SelectedFunction.ToString)

    End Sub


    Private Sub RXBufferCheck(Bytesavail As Integer) Handles pport.DataReceived
        If RXStatusControl IsNot Nothing Then
            'Dim CtrlVAlues As New List(Of PropertyPairs) From {New PropertyPairs With {.Name = "Text", .Value = Bytesavail.ToString}}
            RefreshControl(RXStatusControl, New List(Of PropertyPairs) From {New PropertyPairs With {.Name = "Text", .Value = Bytesavail.ToString}})
        End If
        RaiseEvent DataReceived(Bytesavail)

    End Sub


    Private Sub ConnectionStateChg(newstate As ConnectionStates) Handles Me.constatechanged
        Dim updatetext As String = Nothing
        ConnectionState = newstate
        If RXKrackerFWControl IsNot Nothing Then
            Select Case newstate
                Case ConnectionStates.Connecting
                    updatetext = "Connecting on COM" & ComPort.ToString
                Case ConnectionStates.KrackerFW
                    updatetext = "Car Kracker on COM" & ComPort.ToString
                Case ConnectionStates.Searching
                    updatetext = "Connect Car Kracker"
                Case ConnectionStates.OtherFW
                    updatetext = "Update Firmware"
                Case ConnectionStates.Disconnected
                    updatetext = "Unexpected Disconnection"
            End Select
            'Dim CtrlVAlues As New List(Of PropertyPairs) From {New PropertyPairs With {.Name = "Text", .Value = updatetext}}
            RefreshControl(RXKrackerFWControl, New List(Of PropertyPairs) From {New PropertyPairs With {.Name = "Text", .Value = updatetext}})
        Else
            Debug.WriteLine("NO controlset")
        End If
        setdebugstrings()
        RaiseEvent ConnectionStateChanged(newstate)
    End Sub

    Public Sub setdebugstrings()
        If ConnectionState = ConnectionStates.KrackerFW Then

            If KrackerFWNum > 0.79 Then
                KrackerMode = KrackerModes.Config
                Dim strversion = xmldoc.<config>.<stringlist>.@version
                Debug.WriteLine("String version stored :" & strversion.ToString)
                If GetRemote(999) < Convert.ToInt16(strversion) Then
                    SerialLineOut("M5")
                    Dim debugquery = From debugitem In xmldoc.<config>.<stringlist>.<string>
                    debugquery.ToList.ForEach(Sub(debugitem)
                                                  SerialLineOut("Ss" & debugitem.Attribute("strindex").Value)
                                                  SerialLineOut(debugitem.Value)
                                                  Debug.WriteLine("Buffer size: " & ReadSerialLine("&&"))
                                              End Sub)

                    debugquery = From debugitem In xmldoc.<config>.<stringlist>.<cmd>
                    debugquery.ToList.ForEach(Sub(debugitem)
                                                  SerialLineOut("S" & debugitem.Attribute("type").Value & debugitem.Attribute("idx").Value)
                                                  SerialLineOut(debugitem.Value)
                                                  Debug.WriteLine("Buffer size: " & ReadSerialLine("&&"))
                                              End Sub)
                    setremote(999, strversion)
                End If
            End If
        End If


    End Sub


    Public Sub RefreshControl(TargetControl As Object, NewControl As List(Of PropertyPairs))
        Dim newtgt As Control = CType(TargetControl, Control)
        If newtgt.InvokeRequired Then
            Dim x As New ControlRefreshDelegate(AddressOf RefreshControl)
            HostForm.Invoke(x, New Object() {TargetControl, NewControl})
        Else
            Dim targettype As Type = TargetControl.GetType
            For i As Integer = 0 To NewControl.Count - 1
                Dim SourceProperty As PropertyInfo = targettype.GetProperty(NewControl.Item(i).Name.ToString)
                SourceProperty.SetValue(TargetControl, NewControl.Item(i).Value, Nothing)
            Next
        End If

    End Sub

    Private Sub SerialPort_Watchdog(ByVal Flag As EnhancedSerialPort.SerialPort.WatchdogFlags) Handles pport.Watchdog

        If Flag = EnhancedSerialPort.SerialPort.WatchdogFlags.Timeout Then
            RaiseEvent constatechanged(ConnectionStates.Disconnected)
        End If

    End Sub

    Public Function readline() As String
        Return pport.ReadLine()
    End Function

    Private Sub resetPropeller()
        pport.DTREnable = True
        Thread.Sleep(100)
        pport.DTREnable = False

    End Sub

    Public Sub loaddrops()
        Debug.WriteLine("Loading Dropdowns")
        Dim activedrop, activetab As String

        Dim ob As Object = Nothing
        Dim resultlist As Generic.IEnumerable(Of XElement)
        Dim debugstr As String = Nothing
        Dim dropsquery = From dropitem In xmldoc.<config>.<dropdowns>.<list>

        For Each dropitem In dropsquery
            activetab = dropitem.Attribute("page").Value
            activedrop = dropitem.Attribute("name").Value

            Dim controltest() As Control = HostForm.Controls.Find(activedrop, True)
            If controltest(0) IsNot Nothing Then
                ob = controltest(0)
                ob.text = dropitem.<label>.Value
                HostForm.ToolTip1.SetToolTip(ob, dropitem.<tooltip>.Value)


                If dropitem.Attribute("linkedlist") IsNot Nothing Then
                    Dim linkedlistname As String = dropitem.Attribute("linkedlist").Value
                    Dim subdropquery = From duplist In xmldoc.<config>.<selectionlists>.<list> Where duplist.@name = linkedlistname
                    ob.text = subdropquery.<label>.Value
                    If HostForm.ToolTip1.GetToolTip(ob) = "" Then
                        HostForm.ToolTip1.SetToolTip(ob, subdropquery.<tooltip>.Value)
                    End If
                    resultlist = subdropquery.<listitems>.<selectable>
                Else
                    resultlist = dropitem.<listitems>.<selectable>
                End If
                resultlist.ToList.ForEach(Sub(listelement) ob.items.add(If((listelement.Attribute("value") IsNot Nothing), (New clCombobox(listelement.Value, listelement.Attribute("value").Value)), (listelement.Value))))
                resultlist = Nothing
            End If
        Next
    End Sub


    Protected Overrides Sub Finalize()
        PropellentDLL.QuitProp()
        MyBase.Finalize()
    End Sub
    Enum ConnectionStates As Integer
        NotLoaded
        Disconnected
        PropellentLoaded
        Searching
        Connecting
        KrackerFW
        OtherFW

    End Enum
    Enum KrackerModes As Integer
        NotAvailable
        Config
        PCdiag
        SerialMonitor
    End Enum
    Enum HousekeepingFunctions
        LightSwitch = 6
        FlipInteractiveMode = 9
        ResetMemorystorage = 5
    End Enum


End Class





Public Class PropellentDLL
    <DllImport("resources/Propellent.dll", EntryPoint:="InitPropellent", CallingConvention:=CallingConvention.Cdecl)>
    Private Shared Sub PrivateStart(ByVal hw As IntPtr, ByVal sp As Boolean, ByVal rp As IntPtr)
    End Sub

    <DllImport("resources/Propellent.dll", EntryPoint:="DownloadToPropeller", CallingConvention:=CallingConvention.Cdecl)>
    Private Shared Sub PrivateflashProp(ByVal filename As IntPtr, ByVal flashmode As Byte)
    End Sub

    <DllImport("resources/Propellent.dll", EntryPoint:="GetLibraryVersion", CallingConvention:=CallingConvention.Cdecl)>
    Private Shared Function PrivateVersionCheck() As UInteger
    End Function

    <DllImport("resources/Propellent.dll", EntryPoint:="SetGUIMode", CallingConvention:=CallingConvention.Cdecl)>
    Private Shared Sub PrivateSetGUI(ByVal gmode As Byte)
    End Sub

    <DllImport("resources/Propellent.dll", EntryPoint:="FinalizePropellent", CallingConvention:=CallingConvention.Cdecl)>
    Private Shared Sub PrivateQuitProp()
    End Sub

    <DllImport("resources/Propellent.dll", EntryPoint:="GetPropellerVersion", CallingConvention:=CallingConvention.Cdecl)>
    Private Shared Function PrivatePropVersion() As IntPtr
    End Function

    <DllImport("resources/Propellent.dll", EntryPoint:="SetSerialSearchMethod", CallingConvention:=CallingConvention.Cdecl)>
    Private Shared Sub PrivateSetSerialSearch(ByVal SearialSearch As Integer)
    End Sub
    ''' <summary>
    ''' Write Binary to Propeller EEPROM or Ram
    ''' </summary>
    ''' <param name="filename">Filename, from assembly directory</param>
    ''' <param name="Mode">Update to RAM or EEPROM</param>
    ''' <remarks></remarks>
    Public Shared Sub flashProp(ByVal filename As String, ByVal Mode As Flashmode)
        Dim selectedptr As IntPtr = Marshal.StringToHGlobalAnsi(filename)
        If PropellentStatus = Status.Loaded Then
            PrivateflashProp(selectedptr, Convert.ToByte(Mode))
        End If
    End Sub
    ''' <summary>
    ''' Initialize Propellent Library
    ''' </summary>
    ''' <param name="WindowHandle">Handle to Main Program Window</param>
    ''' <param name="storeprefs">True to use Registry for Preferenece storage</param>
    ''' <param name="RegistryPath">Set RegPath to null to use the default path. Set RegPath to the address of a path string to have the library automatically store its settings in another path (such as your application's registry path); a new key will automatically be appended to the path.</param>
    ''' <remarks>Call only once.  Call Quit when application closes</remarks>
    Public Shared Sub Start(ByVal WindowHandle As IntPtr, ByVal storeprefs As Boolean, ByVal RegistryPath As IntPtr)
        If PropellentStatus = Status.NotLoaded Then
            PrivateStart(WindowHandle, storeprefs, RegistryPath)
            PropellentStatus = Status.Loaded
        End If
    End Sub
    Public Shared Sub QuitProp()
        If PropellentStatus = Status.Loaded Then
            PrivateQuitProp()
            PropellentStatus = Status.NotLoaded
        End If
    End Sub

    Public Shared Sub SetGUI(ByVal gmode As GuiMode)
        'If PropellentStatus <> Status.Loaded Then
        '    Start(Form.ActiveForm.Handle, False, Nothing)
        'End If

        If PropellentStatus = Status.Loaded Then
            If CurrGUIMode <> gmode Then
                PrivateSetGUI(Convert.ToByte(gmode))
                CurrGUIMode = gmode
            End If
        End If

    End Sub

    Public Shared Function VersionCheck() As UInteger
        If PropellentVersion <> 0 Then
            Return PropellentVersion
        End If

        'If PropellentStatus <> Status.Loaded Then
        '    Start(Form.ActiveForm.Handle, False, Nothing)
        'End If

        If PropellentStatus = Status.Loaded Then
            PropellentVersion = PrivateVersionCheck()
            Return PropellentVersion
        End If
    End Function

    Public Shared Sub setserialsearch(serialsearch As Integer)
        'If PropellentStatus <> Status.Loaded Then
        '    Start(Form.ActiveForm.Handle, False, Nothing)
        'End If

        If PropellentStatus = Status.Loaded Then
            PrivateSetSerialSearch(serialsearch)
        End If
    End Sub


    Public Shared Function PropVersion() As String
        'If PropellentStatus <> Status.Loaded Then
        '    Start(Form.ActiveForm.Handle, False, Nothing)
        'End If

        If PropellentStatus = Status.Loaded Then
            Return Marshal.PtrToStringAnsi(PrivatePropVersion())
        Else
            Return vbEmpty
        End If
    End Function



    Public Enum Flashmode As Integer
        RAMtoRun = 1
        EEPROMandStop = 2
        EEPROMandRun = 3
    End Enum

    Public Enum GuiMode As Integer
        AllOn
        ShowStatus
        ShowDialog
        AllOff
    End Enum

    Public Enum Status As Integer
        NotLoaded
        Loaded
    End Enum
    Private Shared Property PropellentVersion As UInteger

    Private Shared Property _PropellentStatus As Status
    Public Shared Property PropellentStatus As Status
        Get
            Return _PropellentStatus
        End Get
        Private Set(value As Status)
            _PropellentStatus = value
        End Set
    End Property
    Private Shared Property CurrGUIMode As GuiMode
End Class




Public Class PropertyPairs
    Property Name As String
    Property Value As Object
    Property delay As Integer
    Property Search As Byte
End Class



